package com.ajavaer.framework.pay;

import com.ajavaer.framework.common.message.Message;
import com.ajavaer.framework.common.tools.JsonTools;
import com.ajavaer.framework.common.tools.ServletTools;
import com.ajavaer.framework.common.tools.StringTools;
import com.ajavaer.framework.config.WeixinPayConfig;
import com.ajavaer.framework.handle.WeixinPayHandle;
import com.ajavaer.framework.pay.request.*;
import com.ajavaer.framework.pay.response.*;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

/**
 * microPay	刷卡支付
 * unifiedOrder	统一下单
 * orderQuery	查询订单
 * reverse	撤销订单
 * closeOrder	关闭订单
 * refund	申请退款
 * refundQuery	查询退款
 * downloadBill	下载对账单
 * report	交易保障
 * shortUrl	转换短链接
 * authCodeToOpenid	授权码查询openid
 */
@Component
public class WeixinPay implements InitializingBean {
    private Logger log = LoggerFactory.getLogger(WeixinPay.class);
    @Resource
    private WeixinPayConfig weixinPayConfig;
    @Resource
    private Environment environment;
    private Boolean isDebug = true;

    private WXPay wxPay;

    @Override
    public void afterPropertiesSet() throws Exception {
        wxPay = new WXPay(weixinPayConfig.toWXPayConfig(), weixinPayConfig.getNotifyUrl(), true, weixinPayConfig.getUseSandbox());
        this.isDebug = this.environment.getProperty("ajavaer.debug", Boolean.class, true);
        if (isDebug) {
            log.debug("Pay Weixin init");
        }
    }

    /**
     * 统一下单
     *
     * @param weixinUnifiedOrderRequest
     * @return
     */
    public Message<WeixinUnifiedOrderResponse> unifiedOrder(WeixinUnifiedOrderRequest weixinUnifiedOrderRequest) {
        try {
            if (isDebug) {
                log.debug("[Pay Weixin] unifiedOrder params:" + JsonTools.beanToJson(weixinUnifiedOrderRequest));
            }
            Map<String, String> resp = wxPay.unifiedOrder(weixinUnifiedOrderRequest.toMap());
            WeixinUnifiedOrderResponse response = new WeixinUnifiedOrderResponse();
            BeanUtils.populate(response, resp);
            return Message.successResult(response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Message.fail(e.getMessage());
        }
    }

    /**
     * app下单后,使用prepayid生成带签名数据
     *
     * @param prepayId
     * @return
     */
    public WeixinAppOrderResponse appOrderSignResponse(String prepayId) {
        //计算签名
        Map<String, String> doWeixinPay = new HashMap<>();
        doWeixinPay.put("appid", weixinPayConfig.getAppid());
        doWeixinPay.put("partnerid", weixinPayConfig.getMchid());
        doWeixinPay.put("prepayid", prepayId);
        doWeixinPay.put("package", "Sign=WXPay");
        doWeixinPay.put("timestamp", WXPayUtil.getCurrentTimestamp() + "");
        doWeixinPay.put("noncestr", WXPayUtil.generateNonceStr());
        String sign = null;
        try {
            sign = WXPayUtil.generateSignature(doWeixinPay, weixinPayConfig.getSecret(), WXPayConstants.SignType.HMACSHA256);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //构造返回
        WeixinAppOrderResponse response = new WeixinAppOrderResponse();
        response.setAppid(weixinPayConfig.getAppid());
        response.setPartnerid(weixinPayConfig.getMchid());
        response.setPrepayid(prepayId);
        response.setNoncestr(WXPayUtil.generateNonceStr());
        response.setTimestamp(WXPayUtil.getCurrentTimestamp() + "");
        response.setPackageValue("Sign=WXPay");
        response.setSign(sign);
        return response;
    }

    /**
     * 订单查询
     *
     * @param weixinOrderQueryRequest
     * @return
     */
    public Message<WeixinOrderQueryResponse> orderQuery(WeixinOrderQueryRequest weixinOrderQueryRequest) {
        try {
            if (isDebug) {
                log.debug("[Pay Weixin] orderQuery params:" + JsonTools.beanToJson(weixinOrderQueryRequest));
            }
            Map<String, String> resp = wxPay.orderQuery(weixinOrderQueryRequest.toMap());
            WeixinOrderQueryResponse response = new WeixinOrderQueryResponse();
            BeanUtils.populate(response, resp);
            return Message.successResult(response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Message.fail(e.getMessage());
        }
    }

    /**
     * 关闭订单
     * <p>
     * 以下情况需要调用关单接口：商户订单支付失败需要生成新单号重新发起支付，要对原订单号调用关单，避免重复支付；系统下单后，用户支付超时，系统退出不再受理，避免用户继续，请调用关单接口。
     * 注意：订单生成后不能马上调用关单接口，最短调用时间间隔为5分钟。
     * </p>
     *
     * @param weixinCloseOrderRequest
     * @return
     */
    public Message<WeixinCloseOrderResponse> closeOrder(WeixinCloseOrderRequest weixinCloseOrderRequest) {
        try {
            if (isDebug) {
                log.debug("[Pay Weixin] closeOrder params:" + JsonTools.beanToJson(weixinCloseOrderRequest));
            }
            Map<String, String> resp = wxPay.closeOrder(weixinCloseOrderRequest.toMap());
            WeixinCloseOrderResponse response = new WeixinCloseOrderResponse();
            BeanUtils.populate(response, resp);
            return Message.successResult(response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Message.fail(e.getMessage());
        }
    }

    /**
     * 支付回调
     *
     * @param weixinPayHandle
     */
    public <T> void payCallBack(HttpServletRequest request, HttpServletResponse response, WeixinPayHandle<T> weixinPayHandle) throws Exception {
        String res = ServletTools.getRequestBody(request);
        if (StringTools.isBlank(res)) {
            ServletTools.printString(response, WXPayUtil.mapToXml(WeixinMsgResponse.fail("数据为空").toMap()));
            return;
        }
        Map<String, String> data = WXPayUtil.xmlToMap(res);
        if (isDebug) {
            log.debug("[Pay Weixin] payCallBack params:" + JsonTools.beanToJson(data));
        }
        weixinPayHandle.onMessage(data);
        WeixinOrderQueryResponse weixinOrderQueryResponse = new WeixinOrderQueryResponse();
        BeanUtils.populate(weixinOrderQueryResponse, data);
        boolean signatureValid = WXPayUtil.isSignatureValid(data, weixinPayConfig.getSecret(), WXPayConstants.SignType.HMACSHA256);
        if (!signatureValid) {
            log.error("wxPayCallBack sign fail:{}", res);
            ServletTools.printString(response, WXPayUtil.mapToXml(WeixinMsgResponse.fail("签名错误").toMap()));
            return;
        }
        T orderEntity = weixinPayHandle.getByOutTradeNo(weixinOrderQueryResponse.getOut_trade_no());
        boolean checkFee = weixinPayHandle.checkOrderByAmount(orderEntity, new BigDecimal(weixinOrderQueryResponse.getTotal_fee()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
        if (!checkFee) {
            log.error("wxPayCallBack total_fee fail:{}", res);
            ServletTools.printString(response, WXPayUtil.mapToXml(WeixinMsgResponse.fail("金额错误").toMap()));
            return;
        }

        String returnCode = weixinOrderQueryResponse.getReturn_code();
        if (StringTools.isNotBlank(returnCode) && returnCode.equalsIgnoreCase("FAIL")) {
            log.error("wxPayNotice returnCode fail msg:" + JsonTools.beanToJson(response));
            return;
        }
        String appid = weixinPayConfig.getAppid();
        if (StringTools.isBlank(appid) || !weixinOrderQueryResponse.getAppid().equalsIgnoreCase(appid)) {
            log.error("appid or mchId is not match " + JsonTools.beanToJson(response));
            return;
        }
        String mchId = weixinPayConfig.getMchid();
        if (StringTools.isNotBlank(mchId) && !weixinOrderQueryResponse.getMch_id().equalsIgnoreCase(mchId)) {
            log.error("mchId dose not match " + JsonTools.beanToJson(response));
        }
        String resultCode = weixinOrderQueryResponse.getResult_code();
        if (StringTools.isNotBlank(resultCode) && resultCode.equalsIgnoreCase("SUCCESS")) {
            weixinPayHandle.business(orderEntity, weixinOrderQueryResponse);
        }
    }

    /**
     * 退款
     */
    public Message<WeixinRefundResponse> refund(WeixinRefundRequest weixinRefundRequest) throws Exception {
        if (isDebug) {
            log.debug("[Pay Weixin] refund params:" + JsonTools.beanToJson(weixinRefundRequest));
        }
        WeixinRefundResponse response = new WeixinRefundResponse();
        Map<String, String> refund = wxPay.refund(weixinRefundRequest.toMap());
        BeanUtils.populate(response, refund);
        return Message.successResult(response);
    }

    /**
     * 退款查询
     *
     * @param request
     * @return
     * @throws Exception
     */
    public Message<WeixinRefundQueryResponse> refundQuery(WeixinRefundQueryRequest request) throws Exception {
        if (isDebug) {
            log.debug("[Pay Weixin] refundQuery params:" + JsonTools.beanToJson(request));
        }
        Map<String, String> refundQuery = wxPay.refundQuery(request.toMap());
        return Message.successResult(new WeixinRefundQueryResponse(refundQuery));
    }

}
